﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Game.Dialogs;
using System.IO;
using Game.Classes;

namespace Game
{
    public partial class Editor : Form
    {
        #region Initialisieren
        //Tileset Objekt erstellen
        Tileset     tileset     = new Tileset();        //Tiles laden
        Size        mapsize     = new Size(20, 20);     //Standartgröße 20x20
        String      tool        = String.Empty;         //Ausgewähltes Tool

        //Spielerpositionen
        List<Point> playerPositions = new List<Point>();
        int         playerCount     = 2;

        //Truhe position
        Point chestPos = new Point(1,1);

        //Tilerotation
        string tileRotation = "N";

        bool clickHold = false;

        //Arrays für die beiden Maps (Vorder und Hintergrund) erstellen
        public string[][] fgmap;
        public string[][] bgmap = new string[][]{
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"},
                new string[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0"}
       };

        //Hintergrund variablen
        Point       lastMousePosBg; //Feld auf dem die Maus als letztes war
        Graphics    g_bg;           //Graphics objekt auf dem gezeichnet wird
        Image       img_bg;         //Imageobjekt das einer Picturebox zugewiesen wird

        //Vordergrund variablen
        Image       imgMap;         //Imageobjekt welches der Picturebox zugewiesen wird
        Graphics    g_fg;           //Graphicsobjekt auf dem gezeichnet wird
        Point       lastMousePosFg; //Feld auf dem die Maus als letztes war
       
        //Konstruktor mit größe
        public Editor(Size mapsize)
        {
            InitializeComponent();
            this.mapsize = mapsize;

            //Comboboxen Selected setzen
            cbPlacePlayer.SelectedIndex = 0;
            cbPlayerCount.SelectedIndex = 0;

            //Spielerpositionen hinzufügen
            for (int x = 0; x < 8; x++)
                playerPositions.Add(new Point(1, 1));


            //Picturebox Größe anpassen
            pbMap.Width     = mapsize.Width * 64;
            pbMap.Height    = mapsize.Height * 64;

            //Hintergrund Image, Graphics und Position initialisieren
            img_bg          = new Bitmap(pbBG.Size.Width, pbBG.Size.Height);
            g_bg            = Graphics.FromImage(img_bg);
            lastMousePosBg  = new Point(0, 0);


            //Vordergrund Image, Graphics und Position initialisieren
            imgMap          = new Bitmap(pbMap.Size.Width, pbMap.Size.Height);
            g_fg            = Graphics.FromImage(imgMap);
            lastMousePosFg  = new Point(0, 0);


            //Vordergrund Map wird mit standartwert "0" aufgebaut
            fgmap = new string[mapsize.Height][];
            for (int i = 0; i < mapsize.Height; i++)
            {
                fgmap[i] = new string[mapsize.Width];
                for (int x = 0; x < mapsize.Width; x++)
                    fgmap[i][x] = "0";
            }


            //ImageListe wird benötigt um vorschaubilder darzustellen
            ImageList imageList = new ImageList();

            //Bilder aller Tiles in Imagelist einfügen
            foreach (Tile t in tileset.tiles)
            {
                imageList.Images.Add("ID: " + t.getID(), t.getImage());
            }

            //Imagelist mit der ListView verbinden
            lvTiles.LargeImageList = imageList;

            //Namen (ID's) der Tiles in der ListView hinzufügen
            for (int i = 0; i < imageList.Images.Count; i++)
            {
                ListViewItem lvi = new ListViewItem();
                lvi.ImageIndex = i;
                lvi.Text = tileset.tiles[i].getID();
                Tile tile = tileset.tiles[i];
                lvTiles.Items.Add(lvi);
                addtoGroup(lvi, tile.Group);
            }
        }
        #endregion

        #region Events
     
        //Spielerzahl festlegen
        private void cbPlayerCount_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(cbPlayerCount.SelectedText))
                playerCount = int.Parse(cbPlayerCount.SelectedText);
            else
                playerCount = 2;
        }

        //Tool auswahl aufheben wenn ein Tile gewählt wird
        private void lvTiles_SelectedIndexChanged(object sender, EventArgs e)
        {
            tool = "";
            tileRotation = "N";
        }



        //Mausbewegung auf dem Hintergrund abfangen
        private void pbBG_MouseMove(object sender, MouseEventArgs e)
        {
            setlastMousePos("bg", e.X, e.Y);

            if (clickHold)
                pbBG_MouseClick(null, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
        }

        //Mausbewegung auf dem Vordergrund abfangen
        private void pbMap_MouseMove(object sender, MouseEventArgs e)
        {
            setlastMousePos("fg", e.X, e.Y);

            if (clickHold)
                pbMap_MouseClick(null, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0));
        }



        //MouseDown Lock für Drag and Drop
        private void pbMap_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                clickHold = true;

            tabPage2.Focus();
        }

        private void pbMap_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                clickHold = false;
        }

        private void pbBG_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                clickHold = true;
        }

        private void pbBG_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                clickHold = false;
        }



        //Mausklick auf Hintergrund Map
        private void pbBG_MouseClick(object sender, MouseEventArgs e)
        {
            //Rechtsklick = Rotieren
            if (e.Button == MouseButtons.Right)
                rotate();

            //Falls ein Tile in der Liste ausgewählt ist so wird dieses plaziert
            //ansonsten wird das Feld wieder auf den Standartwert 0 gesetzt
            if (e.Button == MouseButtons.Left)
                if (lvTiles.SelectedIndices.Count > 0)
                {
                    Tile tile;
                    if (cbRanomize.Checked)
                    {
                        tile = tileset.getTileByID(lvTiles.SelectedItems[0].Text);
                        tile = tileset.getRandomGroupID(tile);
                    }
                    else
                        tile = tileset.getTileByID(lvTiles.SelectedItems[0].Text);


                    bgmap[lastMousePosBg.Y / 64][lastMousePosBg.X / 64] = tile.getID() + ":" + tileRotation;
                }
                else
                {
                    bgmap[lastMousePosBg.Y / 64][lastMousePosBg.X / 64] = "0";
                }
        }

        //Mausklick auf VordergrundMap
        private void pbMap_MouseClick(object sender, MouseEventArgs e)
        {
            //Rechtsklick = Rotieren
            if (e.Button == MouseButtons.Right)
                rotate();

            //Platzieren vom ausgewählten Tile, Spieler....
            if (e.Button == MouseButtons.Left)
            {
                //Falls ein Tile in der Liste ausgewählt ist so wird dieses plaziert
                //ansonsten wird das Feld wieder auf den Standartwert 0 gesetzt
                if (tool.StartsWith("P"))
                    playerPositions[cbPlacePlayer.SelectedIndex] = new Point(lastMousePosFg.X / 64, lastMousePosFg.Y / 64);

                else if (tool.StartsWith("chest"))
                    chestPos = new Point(lastMousePosFg.X / 64, lastMousePosFg.Y / 64);

                else if (lvTiles.SelectedIndices.Count > 0)
                {
                    Tile tile;
                    if (cbRanomize.Checked)
                    {
                        tile = tileset.getTileByID(lvTiles.SelectedItems[0].Text);
                        tile = tileset.getRandomGroupID(tile);
                    }
                    else
                        tile = tileset.getTileByID(lvTiles.SelectedItems[0].Text);

                    fgmap[lastMousePosFg.Y / 64][lastMousePosFg.X / 64] = tile.getID() + ":" + tileRotation;
                }
                else
                {
                    fgmap[lastMousePosFg.Y / 64][lastMousePosFg.X / 64] = "0";
                }
            }
        }

        //Neue Map erstellen
        private void neuToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Session.frmModal = new NewMap();
            Session.frmModal.Show();
        }

        //Editor Beenden
        private void beendenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        //Löschen Tool Aktivieren
        private void btDel_Click(object sender, EventArgs e)
        {
            //Auswahl aufheben
            foreach (ListViewItem i in lvTiles.Items)
                i.Selected = false;
            tool = "";
        }

        //Löschen Tool Aktivieren
        private void btChest_Click(object sender, EventArgs e)
        {
            //Auswahl aufheben
            foreach (ListViewItem i in lvTiles.Items)
                i.Selected = false;
            tool = "chest";
        }

        //Tool auswählen um Spieler zu platzieren
        private void btPlayer1_Click(object sender, EventArgs e)
        {
            int z = cbPlacePlayer.SelectedIndex;
            tool = "P" + (z + 1);
        }

        #endregion

        #region Tools
        //Vordergrund leeren
        private void vordergrundLeerenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("Vordergrund wirklich löschen?", "Wirklich leeren?", MessageBoxButtons.YesNo, MessageBoxIcon.Information);

            if (dr == DialogResult.Yes)
            {
                for (int y = 0; y < fgmap.Length; y++)
                    for (int x = 0; x < fgmap[0].Length; x++)
                        fgmap[y][x] = "0";
            }
        }

        //Hintergrund leeren
        private void hintergrundLeerenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("Hintergrund wirklich löschen?", "Wirklich leeren?", MessageBoxButtons.YesNo, MessageBoxIcon.Information);

            if (dr == DialogResult.Yes)
            {
                for (int y = 0; y < bgmap.Length; y++)
                    for (int x = 0; x < bgmap[0].Length; x++)
                        bgmap[y][x] = "0";
            }
        }

        //Letzte Mausposition setzen
        private void setlastMousePos(string layer, int x, int y)
        {
            int pX = (x / 64) * 64;
            int pY = (y / 64) * 64;

            pX = pX < 0 ? 0 : pX;
            pY = pY < 0 ? 0 : pY;

            if (layer == "bg")
            {
                pX = pX > 1023 ? 1023 : pX;
                pY = pY > 767 ? 767 : pY;

                lastMousePosBg = new Point(pX, pY);
            }
            else
            {
                pX = pX > fgmap[0].Length   * 64 - 1 ? fgmap[0].Length  * 64 - 1 : pX;
                pY = pY > fgmap.Length      * 64 - 1 ? fgmap.Length     * 64 - 1 : pY;

                lastMousePosFg = new Point(pX, pY);
            }    
        }

        //Info labels Updaten
        public void updateLabels()
        {
            //Labels Updaten
            if (tabControl1.SelectedIndex == 0)
                lblPos.Text = "X: " + lastMousePosBg.X / 64 + ", Y: " + lastMousePosBg.Y / 64;
            else
                lblPos.Text = "X: " + lastMousePosFg.X / 64 + ", Y: " + lastMousePosFg.Y / 64;

            if (tool.StartsWith("P"))
                lblTool.Text = "Spieler " + (cbPlacePlayer.SelectedIndex + 1);

            if (tool.StartsWith("chest"))
                lblTool.Text = "Truhe setzen";

            if (String.IsNullOrEmpty(tool) && lvTiles.SelectedItems.Count < 1)
                lblTool.Text = "Löschen";

            if (String.IsNullOrEmpty(tool) && lvTiles.SelectedItems.Count > 0)
                lblTool.Text = "Tile Platzieren";
        }

        //Map aus Datei in Editor laden
        private void ladenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.RestoreDirectory = true;
            dialog.InitialDirectory = Path.GetFullPath("./Files/Maps");
            dialog.Filter = "map files (*.map)|*.map|All files (*.*)|*.*";

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                string[] lines = File.ReadAllLines(dialog.FileNames[0]);

                string[] config = lines[0].Split(';');

                //Spielerzahl
                playerCount = int.Parse(config[0]);

                //Map höhe und breite
                mapsize = new Size(int.Parse(config[1]), int.Parse(config[2]));
                pbMap.Width = mapsize.Width * 64;
                pbMap.Height = mapsize.Height * 64;

                imgMap = new Bitmap(pbMap.Size.Width, pbMap.Size.Height);
                g_fg = Graphics.FromImage(imgMap);

                //Truhenposition setzen
                chestPos = new Point(int.Parse(config[3]), int.Parse(config[4]));

                //Spielerpositionen laden
                for (int ix = 0; ix < playerPositions.Count; ix++)
                    playerPositions[ix] = new Point(1, 1);

                string[] positions = lines[1].Split(';');

                for (int ix = 0; ix < positions.Length; ix++)
                    if (!String.IsNullOrEmpty(positions[ix]))
                    {
                        string[] split = positions[ix].Split(':');
                        playerPositions[ix] = new Point(int.Parse(split[0]), int.Parse(split[1]));
                    }

                //Map erstellen und aus Datei laden
                fgmap = new string[mapsize.Height][];
                for (int i = 0; i < mapsize.Height; i++)
                {
                    fgmap[i] = new string[mapsize.Width];
                    for (int x = 0; x < mapsize.Width; x++)
                    {
                        string[] ids = lines[2 + i].Split(';');
                        fgmap[i][x] = ids[x];
                    }
                }

                //Hintergrund laden
                bgmap = new string[12][];
                for (int i = 0; i < 12; i++)
                {
                    bgmap[i] = new string[16];
                    for (int x = 0; x < 16; x++)
                    {
                        string[] ids = lines[2 + mapsize.Height + i].Split(';');
                        bgmap[i][x] = ids[x];
                    }
                }


            }
        }

        //Map Speichern
        private void speichernToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.RestoreDirectory = true;
            dialog.InitialDirectory = Path.GetFullPath("./Files/Maps");
            dialog.Filter = "map files (*.map)|*.map|All files (*.*)|*.*";

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                string content = string.Empty;

                //Spielerzahl
                int playerCount = cbPlayerCount.SelectedIndex + 2;

                //Konfiguration Speichern
                content += playerCount + ";" + mapsize.Width + ";" + mapsize.Height + ";" + chestPos.X + ";" + chestPos.Y + ";" + Environment.NewLine;

                for (int ix = 0; ix < playerCount; ix++)
                    content += playerPositions[ix].X + ":" + playerPositions[ix].Y + ";";

                content += Environment.NewLine;

                //Vordergrundmap Speichern
                foreach (string[] row in fgmap)
                    content += Tools.ArrayToString(row, ";") + Environment.NewLine;

                //Hintergrundmap Speichern
                foreach (string[] row in bgmap)
                    content += Tools.ArrayToString(row, ";") + Environment.NewLine;

                File.WriteAllText(dialog.FileName, content);
            }


        }

        //Fügt ein ListViewItem einer Gruppe hinzu. Falls diese nicht existiert wird sie erstellt
        public void addtoGroup(ListViewItem item, string groupName)
        {
            bool groupExists = false;

            foreach (ListViewGroup group in lvTiles.Groups)
                if (group.Name == groupName)
                    groupExists = true;

            if (!groupExists)
                lvTiles.Groups.Add(new ListViewGroup(groupName, groupName));

            item.Group = lvTiles.Groups[groupName];
        }

        //Tile Rotieren
        private void rotate()
        {
            if (tileRotation == "N")
                tileRotation = "O";
            else if (tileRotation == "O")
                tileRotation = "S";
            else if (tileRotation == "S")
                tileRotation = "W";
            else
                tileRotation = "N";
        }

        //Hintergrundfläche Zeichnen
        private void renderBGMode()
        {
            //Bild komplett mit Hintergrundfarbe übermalen
            g_bg.Clear(Color.FromArgb(0, 150, 220));

            //Bisherige Hintergrund Tiles einzeichnen
            for (int row = 0; row < bgmap.Length; row++)
                for (int col = 0; col < bgmap[0].Length; col++)
                    if (bgmap[row][col] != "0")
                    {
                        string[] splitted = bgmap[row][col].Split(':');
                        string rotation = "N";
                        string tileID = "0";

                        if (splitted.Length == 1)
                            tileID = splitted[0];
                        else if (splitted.Length == 2)
                        {
                            rotation = splitted[1];
                            tileID = splitted[0];
                        }

                        Image img = (Image)tileset.getTileByID(tileID).getImage().Clone();
                        img = Tools.rotateImage(img, rotation);

                        g_bg.DrawImage(img, new Rectangle(new Point(col * 64, row * 64), new Size(64, 64)));
                    }


            //Vorschau für das Platzieren von Tiles einzeichnen
            if (lvTiles.SelectedIndices.Count <= 0)
                g_bg.FillRectangle(new SolidBrush(Color.FromArgb(0, 120, 240)), new Rectangle(lastMousePosBg, new Size(64, 64)));
            else
            {
                //Vorschau für Tile
                g_bg.FillRectangle(new SolidBrush(Color.FromArgb(0, 150, 220)), new Rectangle(lastMousePosBg, new Size(64, 64)));

                Image img = (Image)tileset.getTileByID(lvTiles.SelectedItems[0].Text).getImage().Clone();
                img = Tools.rotateImage(img, tileRotation);

                g_bg.DrawImage(img, new Rectangle(lastMousePosBg, new Size(64, 64)));
            }

            //"Sonnenstrahlen" einzeichnen
            g_bg.DrawImage(Game.Properties.Resources.shine, new Rectangle(0, 0, 1024, 768));

            //Nachdem alles gezeichnet ist wird das Image wieder der Picturebox zugewiesen
            pbBG.Image = img_bg;
        }

        //Vordergrund Zeichnen
        private void renderFGMode()
        {
            //Bild komplett mit Hintergrundfarbe übermalen
            g_fg.Clear(Color.FromArgb(0, 150, 220));

            //Bisherige Hintergrund einzeichnen
            for (int row = 0; row < fgmap.Length; row++)
                for (int col = 0; col < fgmap[0].Length; col++)
                    if (fgmap[row][col] != "0")
                    {
                        List<string> splitted = Tools.readTile(row, col, fgmap);
                        string rotation = splitted[1];
                        string tileID = splitted[0];

                        Image img = (Image)tileset.getTileByID(tileID).getImage().Clone();
                        img = Tools.rotateImage(img, rotation);

                        g_fg.DrawImage(img, new Rectangle(new Point(col * 64, row * 64), new Size(64, 64)));
                    }


            //Spieler einzeichnen
            for (int ix = 0; ix < cbPlayerCount.SelectedIndex + 2; ix++)
            {
                int pX = playerPositions[ix].X * 64;
                int pY = playerPositions[ix].Y * 64;

                Image pimg  = Tools.getPlayerStandImage(ix);
                pimg        = Tools.cropImage(new Rectangle(0, 0, 64, 64), pimg);

                g_fg.DrawImage(pimg, pX, pY, 64, 64);
            }

            //Truhe einzeichnen
            Image cimg = Tools.cropImage(new Rectangle(64,0,64,64), Game.Properties.Resources.Truhe);
            g_fg.DrawImage(cimg, chestPos.X * 64, chestPos.Y * 64, 64, 64);

            //Vorschau für das Platzieren von Tiles einzeichnen
            if (tool.StartsWith("P"))
            {
                Image pimg  = Tools.getPlayerStandImage(cbPlacePlayer.SelectedIndex);
                pimg        = Tools.cropImage(new Rectangle(0, 0, 64, 64), pimg);

                g_fg.DrawImage(pimg, new Rectangle(lastMousePosFg, new Size(64, 64))); //Spielervorschau
            }
            else if (tool.StartsWith("chest"))
                g_fg.DrawImage(cimg, new Rectangle(lastMousePosFg, new Size(64, 64))); //Truhe
            else if (lvTiles.SelectedIndices.Count <= 0)
                g_fg.FillRectangle(new SolidBrush(Color.FromArgb(0, 120, 240)), new Rectangle(lastMousePosFg, new Size(64, 64)));     //Nichts ausgewählt
            else
            {
                //Vorschau für Tile
                g_fg.FillRectangle(new SolidBrush(Color.FromArgb(0, 150, 220)), new Rectangle(lastMousePosFg, new Size(64, 64)));

                Image img = (Image)tileset.getTileByID(lvTiles.SelectedItems[0].Text).getImage().Clone();
                img = Tools.rotateImage(img, tileRotation);

                g_fg.DrawImage(img, new Rectangle(lastMousePosFg, new Size(64, 64)));
            }

            //Nachdem alles gezeichnet ist wird das Image wieder der Picturebox zugewiesen
            pbMap.Image = imgMap;
        }
        
        #endregion


        //Rendering
        private void mainloop_Tick(object sender, EventArgs e)
        {
            //Hintergrundmodus aktiv
            if (tabControl1.SelectedTab == tabControl1.TabPages[0])
                renderBGMode();
            else
                renderFGMode();

            //Labels Updaten
            updateLabels();
        }   
    }
}
